#include "list.h"
#include "z_memory.h"

struct __list {
    list_iter_t *head;
    list_iter_t *tail;
    size_t nelem;
};

/******************************************************************************/
extern list_t *list_new(size_t a_capacity) {
    list_t *this;

    this = z_calloc(1, sizeof(list_t));
    this->head = 0;
    this->tail = 0;
    this->nelem = 0;
    return this;
}

/******************************************************************************/
extern void list_delete(list_t *a_this) {
    list_iter_t *a_next;
    list_iter_t *a_current;

    if (!a_this)
        return;

    /* Free the iterators */
    if (a_this->nelem > 0) {
        a_current = a_this->head;
        do {
            a_next = a_current->__next;
            z_free(a_current);
            a_current = a_next;
        } while (a_current);
    }

    z_free(a_this);
}

/******************************************************************************/
extern void list_clear(list_t *a_this) {
    a_this->head = 0;
    a_this->tail = 0;
    a_this->nelem = 0;
}

/******************************************************************************/
extern size_t list_get_nelem(list_t *a_this) {
    return (a_this ? a_this->nelem : 0);
}

/******************************************************************************/
extern list_iter_t *list_begin(list_t *a_this) {
    return (a_this ? a_this->head : 0);
}

/******************************************************************************/
extern list_iter_t *list_end(list_t *a_this) {
    return 0;
}

/******************************************************************************/
extern list_iter_t *list_begin_r(list_t *a_this) {
    return (a_this ? a_this->tail : 0);
}

/******************************************************************************/
extern list_iter_t *list_end_r(list_t *a_this) {
    return 0;
}

/******************************************************************************/
extern list_iter_t *list_lookup(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    for (i = a_this->head; i; i = i->__next)
        if (a_data == i->data)
            return i;

    return 0;
}

/******************************************************************************/
extern list_iter_t *list_look_add(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    if ((i = list_lookup(a_this, a_data)) != 0)
        return i;

    return list_add(a_this, a_data);
}

/******************************************************************************/
extern list_iter_t *list_add(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    if ((i = z_calloc(1, sizeof(*i))) == 0)
        return 0;

    i->data = a_data;
    i->list = a_this;
    i->__next = a_this->head;
    i->__prev = 0;
    a_this->head = i;

    if (i->__next) i->__next->__prev = i;
    if (!a_this->tail) a_this->tail = i;

    a_this->nelem++;

    return i;
}

/******************************************************************************/
extern list_iter_t *list_sort_add(list_t *a_this, list_func_t a_func, void *a_data) {
    list_iter_t *i;

    if (!a_this || !a_func) return 0;

    for (i = a_this->head; i; i = i->__next)
        if ((*a_func)(i->data, a_data) > 0)
            break;

    if (i)
        return list_iter_insert(i, a_data);
    else
        return list_append(a_this, a_data);
}

/******************************************************************************/
extern int list_del(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this)
        return -1;

    for (i = a_this->head; i; i = i->__next)
        if (a_data == i->data)
            break;

    if (!i)
        return -1;

    list_iter_del(i);
    return 0;
}

/******************************************************************************/
extern void list_foreach(list_t *a_this, list_func_t a_func, void *a_userdata) {
    list_iter_t *i;
    list_iter_t *j;

    if (!a_this || !a_func)
        return;

    for (i = a_this->head; i; i = j) {
        unsigned int ret;

        i->__foreach = 1;
        ret = (*a_func)(i->data, a_userdata);
        j = i->__next;

        if (i->__foreach == 0)
            list_iter_del(i);
        else
            i->__foreach = 0;

        if (ret) return;
    }
}

/******************************************************************************/
extern void list_foreach_r(list_t *a_this, list_func_t a_func, void *a_userdata) {
    list_iter_t *i;
    list_iter_t *j;

    if (!a_this || !a_func)
        return;

    for (i = a_this->tail; i; i = j) {
        unsigned int ret;

        i->__foreach = 1;
        ret = (*a_func)(i->data, a_userdata);
        j = i->__prev;

        if (i->__foreach == 0)
            list_iter_del(i);
        else
            i->__foreach = 0;

        if (ret) return;
    }
}

/******************************************************************************/
extern list_iter_t *list_iter_next(list_iter_t *a_this) {
    return (a_this ? a_this->__next : 0);
}

/******************************************************************************/
extern list_iter_t *list_iter_prev(list_iter_t *a_this) {
    return (a_this ? a_this->__prev : 0);
}

/******************************************************************************/
extern void list_iter_del(list_iter_t *a_this) {
    if (!a_this)
        return;

    if (a_this->__foreach == 1) {
        a_this->__foreach = 0;
        return;
    }

    if (a_this->__next)
        a_this->__next->__prev = a_this->__prev;
    else
        a_this->list->tail = a_this->__prev;

    if (a_this->__prev)
        a_this->__prev->__next = a_this->__next;
    else
        a_this->list->head = a_this->__next;

    a_this->list->nelem--;

    z_free(a_this);
}

/******************************************************************************/
extern list_iter_t *list_iter_insert(list_iter_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    if (a_this->list->head == a_this)
        return list_prepend(a_this->list, a_data);

    if ((i = z_calloc(1, sizeof(*i))) == 0)
        return 0;

    i->data = a_data;
    i->list = a_this->list;
    i->__prev = a_this->__prev;
    i->__next = a_this;

    /* CAUTION: always exists since a_this is not the head */
    a_this->__prev->__next = i;
    a_this->__prev = i;

    a_this->list->nelem++;

    return i;
}

/******************************************************************************/
extern list_iter_t *list_prepend(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    if ((i = z_calloc(1, sizeof(*i))) == 0)
        return 0;

    i->list = a_this;
    i->data = a_data;
    i->__prev = 0;
    i->__next = a_this->head;
    a_this->head = i;

    if (i->__next)
        i->__next->__prev = i;
    else
        a_this->tail = i;

    a_this->nelem++;

    return i;
}

/******************************************************************************/
extern list_iter_t *list_append(list_t *a_this, void *a_data) {
    list_iter_t *i;

    if (!a_this) return 0;

    if ((i = z_calloc(1, sizeof(*i))) == 0)
        return 0;

    i->list = a_this;
    i->data = a_data;
    i->__prev = a_this->tail;
    i->__next = 0;
    a_this->tail = i;

    if (i->__prev)
        i->__prev->__next = i;
    else
        a_this->head = i;

    a_this->nelem++;

    return i;
}
